#include "uifunddetail.h"
#include "ui_uifunddetail.h"

#include <QDebug>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QChartView>
#include <QtCharts>
#include <QDir>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

#include "timetool.h"

static const QString buttonWidgetObjectName = "buttonWidget";
static const QString chartViewWidgetObjectName = "chartViewWidget";
static const QString moreWidgetObjectName = "moreWidget";

static const QString todayValuationQString = "Valuation";
static const QString sevenDaysEarnQString = "7 Days";
static const QString oneMonthEarnQString = "1 Month";
static const QString sixMonthsEarnQString = "6 Months";
static const QString oneYearEarnQString = "1 Year";
static const QString threeYearsEarnQString = "3 Years";

static const QString FUNDData_FOLDER = "fundDatas";
static const QString FUND_SQL_URL_HEAD = "https://gitee.com/Hemingway2003/christopher_fund_data/raw/master/fund/";

static const QString warningGetFundValuationFailedQString = "Get valuation failed, try it again.";

static QPen redPen(Qt::red), greenPen(Qt::green);

static const int DAYS_7_DAYS = 7;
static const int DAYS_1_MONTH = 30;
static const int DAYS_3_MONTHS = 180;
static const int DAYS_1_YEAR = 365;
static const int DAYS_3_YEARS = 3 * 365;


struct __chartViewData {
    QChart *lineChart;
    QChartView *chartView;
//    QValueAxis *xAxis;
    QDateTimeAxis *xAxis;
    QValueAxis *yAxis;
    QLineSeries *spLineSeries;
};

UIFundDetail::UIFundDetail(QString code, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::UIFundDetail)
{
    ui->setupUi(this);
    relocate();
    fundData = new FundData();
    QObject::connect(fundData, SIGNAL(singleFinished(QString, bool)), this, SLOT(gottenSingleData(QString, bool)));
    QObject::connect(fundData, SIGNAL(valuationsFinished(QString, bool)), this, SLOT(gottenValuations(QString, bool)));
    this->code = code;
    // Update in the first time
    fundData->askSingle(code);
    fundData->askValuations(code);
    netFileDownloader = new DatabaseDownloader();
    sqlcipher = new SQLCipher();

    oscillograph = new chartViewData();

    initAllItem();
    bondPushButtonEvent();
    lastQPushButtonQString = QString("NULL");

    lastQPushButtonQString = todayValuationQString;
//    showTodayValuation();
}

UIFundDetail::~UIFundDetail()
{
    deleteAllItem();
    delete oscillograph;
    delete fundData;
    delete netFileDownloader;
    delete ui;
}

void UIFundDetail::gottenSingleData(QString code, bool rslt) {
    if(rslt) {
        if(fundData->getValuations().size() > 0) {
//            qDebug() << fundData->getSingle().timestamp << " " << fundData->getValuations().at(fundData->getValuations().size() - 1).timestamp;
            int gottenTimestamp = fundData->getSingle().timestamp;
            int lastTimestamp = fundData->getValuations().at(fundData->getValuations().size() - 1).timestamp;
            if(gottenTimestamp - lastTimestamp > 10) { // 10 seconds
                valuationData_t singleData;
                singleData.rate = fundData->getSingle().rate;
                singleData.valuation = fundData->getSingle().valuation;
                singleData.timestamp = gottenTimestamp;
                valuationList.append(singleData);
                // Show valuations now
                if(lastQPushButtonQString.compare(todayValuationQString) == 0) {
                    // Clear ui and show
                    oscillograph->spLineSeries->clear();
                    showTodayValuationByList();
                }
            }
        }
        // Update title
        setWindowTitle(fundData->getSingle().name + " [" + fundData->getSingle().code + "]");
        emit updateViewString(code, fundData->getSingle().stringData, fundData->getSingle().rate);
    }
}

void UIFundDetail::gottenValuations(QString code, bool rslt) {
    if(rslt) {
        valuationList = fundData->getValuations();
        showTodayValuationByList();
    }
}

void UIFundDetail::pushButtonClicked() {
    QString text = ((QPushButton *)sender())->text();
//    qDebug() << text;
    if(lastQPushButtonQString.compare(text) == 0 && lastQPushButtonQString.compare(todayValuationQString) != 0) {
        return;
    }

    oscillograph->lineChart->setTitle(text);
    lastQPushButtonQString = text;

    if(text.compare(todayValuationQString) == 0) {
        showTodayValuation();
    } else if(text.compare(sevenDaysEarnQString) == 0) {
        showAccumulatedNet(DAYS_7_DAYS);
    } else if(text.compare(oneMonthEarnQString) == 0) {
        showAccumulatedNet(DAYS_1_MONTH);
    } else if(text.compare(sixMonthsEarnQString) == 0) {
        showAccumulatedNet(DAYS_3_MONTHS);
    } else if(text.compare(oneYearEarnQString) == 0) {
        showAccumulatedNet(DAYS_1_YEAR);
    } else if(text.compare(threeYearsEarnQString) == 0) {
        showAccumulatedNet(DAYS_3_YEARS);
    }
}

void UIFundDetail::buttonLayoutInit() {
    QWidget *buttonWidget = new QWidget();
    buttonWidget->setObjectName(buttonWidgetObjectName);
    QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget);

    QPushButton *buttonResent = new QPushButton(todayValuationQString);
    QPushButton *button7Days = new QPushButton(sevenDaysEarnQString);
    QPushButton *button1Month = new QPushButton(oneMonthEarnQString);
    QPushButton *button6Months = new QPushButton(sixMonthsEarnQString);
    QPushButton *button1Year = new QPushButton(oneYearEarnQString);
    QPushButton *button3Years = new QPushButton(threeYearsEarnQString);

    buttonLayout->addWidget(buttonResent);
    buttonLayout->addWidget(button7Days);
    buttonLayout->addWidget(button1Month);
    buttonLayout->addWidget(button6Months);
    buttonLayout->addWidget(button1Year);
    buttonLayout->addWidget(button3Years);
    buttonLayout->setContentsMargins(0, 0, 0, 0);
    buttonWidget->setLayout(buttonLayout);

    ui->verticalLayout->addWidget(buttonWidget);
}

void UIFundDetail::buttonLayoutDelete(QLayoutItem *item) {
    QWidget *widget = item->widget();
    QHBoxLayout *layout = (QHBoxLayout *)widget->layout();
    QLayoutItem *litem = nullptr;
    while((litem = layout->itemAt(0)) != nullptr) {
        layout->removeItem(litem);
        delete litem->widget();
        delete litem;
    }

    delete layout;
    delete widget;
}

void UIFundDetail::chartViewLayoutInit() {
    oscillograph->lineChart = new QChart();
    oscillograph->chartView = new QChartView();
//    oscillograph->xAxis = new QValueAxis();
    oscillograph->xAxis = new QDateTimeAxis();
    oscillograph->yAxis = new QValueAxis();
    oscillograph->spLineSeries = new QLineSeries();//new QSplineSeries();

    redPen.setWidth(1);
    greenPen.setWidth(1);

    oscillograph->spLineSeries->setPen(redPen);

    oscillograph->lineChart->addSeries(oscillograph->spLineSeries);
    oscillograph->lineChart->addAxis(oscillograph->xAxis,Qt::AlignBottom);
    oscillograph->lineChart->addAxis(oscillograph->yAxis,Qt::AlignLeft);

    oscillograph->spLineSeries->attachAxis(oscillograph->xAxis);
    oscillograph->spLineSeries->attachAxis(oscillograph->yAxis);

//    oscillograph->xAxis->setTickCount(11);
//    oscillograph->xAxis->setRange(0, 10);
//    oscillograph->yAxis->setRange(0, 10);

    oscillograph->lineChart->setTitle(todayValuationQString);
    oscillograph->lineChart->legend()->hide();
    oscillograph->lineChart->setAnimationOptions(QChart::AllAnimations);

    oscillograph->chartView->setObjectName(chartViewWidgetObjectName);
    oscillograph->chartView->setChart(oscillograph->lineChart);
    oscillograph->chartView->setRenderHint(QPainter::Antialiasing);
    oscillograph->chartView->setContentsMargins(0, 0, 0, 0);
    ui->verticalLayout->addWidget(oscillograph->chartView);
}

void UIFundDetail::chartViewLayoutDelete(chartViewData *oscillograph) {
    delete oscillograph->xAxis;
    delete oscillograph->yAxis;
    delete oscillograph->spLineSeries;
    delete oscillograph->lineChart;
    delete oscillograph->chartView;
}

void UIFundDetail::moreTailLayoutInit() {
    QWidget *moreWidget = new QWidget();
    moreWidget->setObjectName(moreWidgetObjectName);
    QHBoxLayout *moreLayout = new QHBoxLayout(moreWidget);
    QPushButton *buttonMore = new QPushButton("More");
    moreLayout->addWidget(buttonMore);
    moreLayout->setContentsMargins(0, 0, 0, 0);
    moreWidget->setLayout(moreLayout);

    ui->verticalLayout->addWidget(moreWidget);
}

void UIFundDetail::moreTailLayoutDelete(QLayoutItem *item) {
    QWidget *widget = item->widget();
    QHBoxLayout *layout = (QHBoxLayout *)widget->layout();
    QLayoutItem *litem = nullptr;
    while((litem = layout->itemAt(0)) != nullptr) {
        layout->removeItem(litem);
        delete litem->widget();
        delete litem;
    }

    delete layout;
    delete widget;
}

void UIFundDetail::initAllItem() {
    buttonLayoutInit();
    chartViewLayoutInit();
    moreTailLayoutInit();
}

void UIFundDetail::showTodayValuation() {
    oscillograph->spLineSeries->clear();

    if(valuationList.size() == 0) {
        fundData->askValuations(code);
        QMessageBox::information(NULL, "Warning", warningGetFundValuationFailedQString, QMessageBox::Ok);
    } else {
        showTodayValuationByList();
    }
}

void UIFundDetail::showTodayValuationByList() {
    double min_xAxis = 0, max_xAxis = 10;
    double min_yAxis = 0, max_yAxis = 10;

    oscillograph->spLineSeries->clear();

    if(valuationList.size() > 0) {
        min_xAxis = valuationList.at(0).timestamp;
        max_xAxis = valuationList.at(valuationList.size() - 1).timestamp;
        min_yAxis = valuationList.at(0).rate;
        max_yAxis = valuationList.at(0).rate;

        oscillograph->xAxis->setFormat("hh:mm");

        if(valuationList.at(valuationList.size() - 1).rate < 0) {
            oscillograph->spLineSeries->setPen(greenPen);
        } else {
            oscillograph->spLineSeries->setPen(redPen);
        }

        for(int i = 0; i < valuationList.size(); i++) {
            valuationData_t data = valuationList.at(i);
            QDateTime data_xAxisTime = QDateTime::fromTime_t(data.timestamp);
            oscillograph->spLineSeries->append(data_xAxisTime.toMSecsSinceEpoch(), data.rate);
            min_yAxis = min_yAxis < data.rate ? min_yAxis : data.rate;
            max_yAxis = max_yAxis > data.rate ? max_yAxis : data.rate;
        }

        // Edit x axis
        QDateTime min_xAxisTime, max_xAxisTime;
        if(max_xAxis <= min_xAxis) {
            max_xAxis = min_xAxis + 10;
        }
        min_xAxisTime = QDateTime::fromTime_t(min_xAxis);
        max_xAxisTime = QDateTime::fromTime_t(max_xAxis);
        oscillograph->xAxis->setRange(min_xAxisTime, max_xAxisTime);

        int xCount = valuationList.size();
        xCount = xCount < 4 ? 4 : xCount;
        xCount = xCount > 11 ? 11 : xCount;
        oscillograph->xAxis->setTickCount(xCount);

        // Edit y axis
        double rate_reserve = (max_yAxis - min_yAxis) / 4;
        rate_reserve = 0; //rate_reserve > 1 ? 1 : rate_reserve;
        if(max_yAxis <= min_yAxis) {
            max_yAxis = min_yAxis + 0.5;
        }
        oscillograph->yAxis->setRange(min_yAxis - rate_reserve, max_yAxis + rate_reserve);
    }
}

bool UIFundDetail::askSQL() {
    QString fund_data_folder_path = FUNDData_FOLDER + "/" + code;
    QString fund_data_path = fund_data_folder_path + + "/" + code;
    // Check fund data mother folder
    QDir mother_dir(FUNDData_FOLDER);
    if(!mother_dir.exists()) {
        QDir().mkdir(FUNDData_FOLDER);
    }
    // single data folder
    QDir fund_dir(fund_data_folder_path);
    if(!fund_dir.exists()) {
        QDir().mkdir(fund_data_folder_path);
    }
    // Check the sql data exits
//    QDir fund_data_dir(fund_data_path);
    QFileInfo fund_data_file(fund_data_path);
    if(!fund_data_file.exists()) {
        qDebug() << code + " sql not exits, downloading...";
        // Ask to download
        netFileDownloader->ask(FUND_SQL_URL_HEAD + code + "/" + code, fund_data_path);
    }

    return true;
}

QList<ACNetData_t> UIFundDetail::getAccumulatedNet(int days) {
    QList<ACNetData_t> list;

    askSQL();
    if(!sqlcipher->open(FUNDData_FOLDER + "/" + code + "/" + code)) {
        qDebug() << code + " open database failed";
//        return list;
    }
    if(!sqlcipher->setKey("xiaowang")) {
        qDebug() << code + " database setkey failed";
        return list;
    }
    if(!sqlcipher->exec("PRAGMA cipher_compatibility = 3;")) {
        qDebug() << code + " database setkey2 failed";
        return list;
    }
    if(!sqlcipher->exec("select all_accumulated_net from fund_data;")) {
        qDebug() << code + " database get data failed";
        return list;
    }

    QJsonParseError jsonError;
    QByteArray jsonArray = sqlcipher->getData().toUtf8();
    QJsonDocument  jsonDoucment = QJsonDocument::fromJson(jsonArray, &jsonError);
    if(jsonError.error != QJsonParseError::NoError) {
        qDebug() << "FundDB get json data failed: " + sqlcipher->getData();
        return list;
    }

    if(jsonDoucment.isArray()) {
        QJsonArray dataArray = jsonDoucment.array();
        int startIndex = dataArray.size() < days ? 0 : dataArray.size() - days;
        for(int i = startIndex; i < dataArray.size(); i++) {
            QJsonArray valueArray = dataArray.at(i).toArray();
            if(valueArray.size() == 2) {
                ACNetData_t ac;
                ac.timestamp = valueArray.at(0).toInt();
                ac.acnet = valueArray.at(1).toDouble();
                list.append(ac);
            }
        }
    }

    return list;
}

void UIFundDetail::showAccumulatedNet(int days) {
    oscillograph->spLineSeries->clear();
    switch(days) {
        case DAYS_7_DAYS:
        {
            ac7DaysNetList = getAccumulatedNet(days);
            showAccumulatedNetByList(ac7DaysNetList);
        }
        break;

        case DAYS_1_MONTH:
        {
            ac1MonthNetList = getAccumulatedNet(days);
            showAccumulatedNetByList(ac1MonthNetList);
        }
        break;

        case DAYS_3_MONTHS:
        {
            ac6MonthsNetList = getAccumulatedNet(days);
            showAccumulatedNetByList(ac6MonthsNetList);
        }
        break;

        case DAYS_1_YEAR:
        {
            ac1YearNetList = getAccumulatedNet(days);
            showAccumulatedNetByList(ac1YearNetList);
        }
        break;

        case DAYS_3_YEARS:
        {
            ac3YearsNetList = getAccumulatedNet(days);
            showAccumulatedNetByList(ac3YearsNetList);
        }
        break;
    }

}

void UIFundDetail::showAccumulatedNetByList(QList<ACNetData_t> list) {
    double min_xAxis = 0, max_xAxis = 10;
    double min_yAxis = 0, max_yAxis = 10;
    oscillograph->spLineSeries->clear();


    if(list.size() > 0) {
        min_xAxis = list.at(0).timestamp;
        max_xAxis = list.at(list.size() - 1).timestamp;
        min_yAxis = list.at(0).acnet;
        max_yAxis = list.at(0).acnet;

        oscillograph->xAxis->setFormat("yy-MM-dd");

        if(list.at(list.size() - 1).acnet < list.at(0).acnet) {
            oscillograph->spLineSeries->setPen(greenPen);
        } else {
            oscillograph->spLineSeries->setPen(redPen);
        }

        for(int i = 0; i < list.size(); i++) {
            ACNetData_t data = list.at(i);
            QDateTime data_xAxisTime = QDateTime::fromTime_t(data.timestamp);
            oscillograph->spLineSeries->append(data_xAxisTime.toMSecsSinceEpoch(), data.acnet);
            min_yAxis = min_yAxis < data.acnet ? min_yAxis : data.acnet;
            max_yAxis = max_yAxis > data.acnet ? max_yAxis : data.acnet;
        }

        // Edit x axis
        QDateTime min_xAxisTime, max_xAxisTime;
        if(max_xAxis <= min_xAxis) {
            max_xAxis = min_xAxis + 10;
        }
        min_xAxisTime = QDateTime::fromTime_t(min_xAxis);
        max_xAxisTime = QDateTime::fromTime_t(max_xAxis);
        oscillograph->xAxis->setRange(min_xAxisTime, max_xAxisTime);

        int xCount = list.size();
        xCount = xCount < 4 ? 4 : xCount;
        xCount = xCount > 11 ? 11 : xCount;
        oscillograph->xAxis->setTickCount(xCount);

        // Edit y axis
        double rate_reserve = (max_yAxis - min_yAxis) / 4;
        rate_reserve = 0; //rate_reserve > 1 ? 1 : rate_reserve;
        if(max_yAxis <= min_yAxis) {
            max_yAxis = min_yAxis + 0.5;
        }
        oscillograph->yAxis->setRange(min_yAxis - rate_reserve, max_yAxis + rate_reserve);
    }
}

void UIFundDetail::deleteAllItem() {
    QLayoutItem *item = nullptr;
    while( (item = ui->verticalLayout->itemAt(0)) != nullptr) {
        QString obName = item->widget()->objectName();
        if(obName.compare(buttonWidgetObjectName) == 0) {
            buttonLayoutDelete(item);
        } else if(obName.compare(chartViewWidgetObjectName) == 0) {
            chartViewLayoutDelete(oscillograph);
        } else if(obName.compare(moreWidgetObjectName) == 0) {
            moreTailLayoutDelete(item);
        }

        ui->verticalLayout->removeItem(item);
        delete item;
    }
}

void UIFundDetail::bondPushButtonEvent() {
    QLayoutItem *item = nullptr;
    // Find button layout first
    while( (item = ui->verticalLayout->itemAt(0)) != nullptr) {
        QString obName = item->widget()->objectName();
        if(obName.compare(buttonWidgetObjectName) == 0) {
            break;
        }
    }

    if(item != nullptr) {
        QWidget *widget = item->widget();
        QHBoxLayout *layout = (QHBoxLayout *)widget->layout();

        for(int i = 0;i < layout->count(); i++) {
            QLayoutItem *litem = layout->itemAt(i);
            QPushButton *button = (QPushButton *)(litem->widget());
            connect(button, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
        }
    }
}

void UIFundDetail::askUpdateViewString() {
    fundData->askSingle(code);
}

void UIFundDetail::resizeEvent(QResizeEvent *event) {
    resize();
}

void UIFundDetail::relocate() {
    ui->verticalLayoutWidget->setGeometry(geometry().x() + 10, geometry().y() + 10, width() - 20, height() - 20);
}
void UIFundDetail::resize() {
    ui->verticalLayoutWidget->resize(width() - 20, height() - 20);

//    oscillograph->xAxis->setTickCount(20);
}
